<?php
/**
 * 数据库mysql驱动
 * 
 * $Id: data_mysql.php 118 2010-05-10 14:45:56Z Administrator $
 * $Revision: 118 $
 * $Author: Administrator $
 * $Date: 2010-05-10 22:45:56 +0800 (星期一, 10 五月 2010) $
 * @author glzaboy<glzaboy@163.com>
 * @package fastlib
 * @subpackage drivers
 * @copyright 2005-2010 (c) glzaboy
 */
if (! defined ( '__FASTLIB__' )) {
	if (__DEBUG__) {
		echo (sprintf ( "script access disallowed at %s in %d.", __FILE__, __LINE__ ));
	} else {
		die ( 'Forbidden 403' );
	}
}
/**
 * Data的Mysql驱动
 *
 * 驱动配置方法
 * 初始化Data实类的传入参数如下
 * <code>
 * $dbconfig=array(
 * "Server"=>"localhost:3306",
 * "pconnect"=>false,//持久连接
 * 'log'   =>true,//输出到日志
 * "logTimeFormat"=>"Y-m-d H:i:s D",//日志时间格式
 * "logFileName"=>date("Y-m-d").".log",//日志文件名
 * "charset"=>"gbk",//数据库编码
 * 'user'  =>'root',
 * 'pwd'   =>'',
 * 'db'   =>'fastlib',
 * 'data_prefix'   =>'job_',//数据库中的前缀
 * 'sql_prefix'   =>'PHPVM_',//SQL中的后缀
 * 'driver'   =>'mysql'//驱动
 * );
 * </code>
 *
 * @author glzaby<glzaboy@163.com>
 */
class data_postgres {
	/**
	 * 日志记录对象
	 *
	 * @var CLASSOBJECT
	 */
	private $logObj = null;
	/**
	 * 数据库连接
	 *
	 * @var Resource
	 */
	private $link = null;
	/**
	 * 数据库出错中止
	 *
	 * @var bool
	 */
	private $halt = false;
	/**
	 * 执行SQL错误信息
	 *
	 * @var string
	 */
	private $sqlErr = null;
	/**
	 * 招待SQL错误代码
	 *
	 * @var int
	 */
	private $sqlErrno = null;
	/**
	 * 返回查询记录数
	 *
	 * @var int
	 */
	private $db = null;
	/**
	 * 正在使用的DB
	 *
	 * @var int
	 */
	private $num_rows = 0;
	/**
	 * 字段左分隔符
	 *
	 * @var string
	 */
	private $left_delimiter = '"';
	/**
	 * 字段右分隔符
	 *
	 * @var string
	 */
	private $right_delimiter = '"';
	/**
	 * 
	 */
	public function __construct() {
		return true;
	}
	/**
	 * 
	 */
	public function __destruct() {
		return true;
	}
	/**
	 * 连接数据库
	 *
	 * @param array $datacfg 数据库连接 
	 * @return int 数据库连接ID或错误
	 */
	public function connect($datacfg) {
		$datacfg ['Server'] = explode ( ':', $datacfg ['Server'] );
		$dbstr = "host= {$datacfg['Server'][0]} dbname={$datacfg['db']} user={$datacfg['user']} password={$datacfg['pwd']}";
		if ($datacfg ['Server'] [1]) {
			$dbstr .= " port={$datacfg['Server'][1]}";
		}
		if (@$datacfg ['pconnect']) {
			$this->link = pg_pconnect ( $dbstr );
			if (! $this->link) {
				$this->putlog ( 'Can\'t connect database use pconnect,Database or database not exists !', __FILE__, __LINE__ );
				$this->halt = true;
				return false;
			}
		} else {
			$this->link = pg_connect ( $dbstr );
			if (! $this->link) {
				$this->putlog ( 'Can\'t connect database use connect,Databaseor database not exists !', __FILE__, __LINE__ );
				$this->halt = true;
				return false;
			}
		}
		$this->db = pg_dbname ( $this->link );
		return $this->link;
	}
	/**
	 * 关闭数据库连接
	 *
	 * @return unknown
	 */
	function close() {
		if ($this->link) {
			pg_close ( $this->link );
		}
		return true;
	}
	/**
	 * 设置编码
	 *
	 * @param string $charset 编码
	 * @return 执行结果
	 */
	public function setCharset($charset) {
		if ($this->halt) {
			$this->putlog ( 'Driver has been halt,please check you config file. ', __FILE__, __LINE__ );
			return null;
		}
		return $this->execSql ( 'Set NAMES \'' . $charset . '\';' );
	}
	/**
	 * 执行SQL语句
	 *
	 * @param string $sql SQL
	 * @return Resource 执行后返回的资源
	 */
	private function execSql($sql) {
		$this->sqlErr = null;
		$this->sqlErrno = null;
		if ($sql == "" or $sql == null) {
			$this->putlog ( 'SQL is empty.', __FILE__, __LINE__ );
			return null;
		}
		$this->putlog ( 'Execute SQL: ' . $sql . '.', __FILE__, __LINE__ );
		if (! $query = pg_query ( $this->link, $sql )) {
			$this->sqlErr = pg_last_error ( $this->link );
			$this->sqlErrno = 0;
			$this->putlog ( 'SQLERR :' . $this->sqlErrno . ' ' . $this->sqlErr, __FILE__, __LINE__ );
		}
		return $query ? $query : null;
	}
	/**
	 * 返回一条数组形式SQL结果
	 *
	 * @param resource $res 数据库SQL查询后的资源
	 * @param string $result_type 返回字段形式，默认是去除数字字段
	 * @return array 单条查询结果
	 */
	private function fetch($resource, $result_type = PGSQL_ASSOC) {
		if ($resource != null) {
			return pg_fetch_array ( $resource, null, $result_type );
		} else {
			return null;
		}
	}
	/**
	 * 生成数据集偏移
	 *
	 * @param int $offset 偏移开始
	 * @param int $num 取得数据数
	 * @return string 数据库查询字符
	 */
	private function makeOffset($offset, $num) {
		return ' limit ' . $offset . ',' . $num;
	}
	/**
	 * 根据joinType返回查询jion字串
	 *
	 * @param string $joinType jion选择类型
	 * @return string SQL中的jion字串
	 */
	private function makeJoin($joinType) {
		switch (strtolower ( $joinType )) {
			case "leftjoin" :
				return " left join ";
				break;
			case "rightjoin" :
				return " right join ";
				break;
			case "crossjoin" :
				return " cross join ";
				break;
			case "innerjoin" :
			default :
				return " INNER join ";
				break;
		}
		return ' ';
	}
	/**
	 * 返回SELECT查询结果
	 * 
	 * 返回形式数组。支持分页，支持多表查询，支持表别名，支持字段别名,支持返回记录数
	 *
	 * @param mixed $matrix 字段数组
	 * @param string $where 查询条件
	 * @param bool $onlynum 是否只返回记录数
	 * @param int $offset 偏移
	 * @param int $num 查取条数
	 * @return mixed 返回记录数组或记录数
	 */
	public function Select($matrix, $where = null, $onlynum = null, $offset = null, $num = null) {
		if ($this->halt) {
			$this->putlog ( 'Driver has been halt,please check you config file. ', __FILE__, __LINE__ );
			return null;
		}
		if (is_string ( $where )) {
			$where = strtr ( $where, array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) );
		}
		if ($offset === null) {
			$offsetStr = '';
		} else {
			$offsetStr = $this->makeOffset ( $offset, $num );
		}
		$sql = '';
		if (is_string ( $matrix )) {
			$sql .= $matrix . ' ' . $where . $offsetStr;
		} elseif (is_array ( $matrix )) {
			$field_list = '';
			$table_list = '';
			foreach ( $matrix as $v ) {
				if (isset ( $v ['join'] ) && $v ['join']) {
					if ($v ['AS']) {
						$table_list .= $this->makeJoin ( $v ['join'] ) . ' ' . $this->left_delimiter . $v ['table'] . $this->right_delimiter . ' AS ' . $this->left_delimiter . $v ['AS'] . $this->right_delimiter;
					} else {
						$table_list .= $this->makeJoin ( $v ['join'] ) . ' ' . $this->left_delimiter . $v ['table'] . $this->right_delimiter;
					}
					$table_list .= 'on  ' . strtr ( $v ['onwhere'], array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) );
				} else {
					if ($v ['AS']) {
						$table_list .= $this->left_delimiter . $v ['table'] . $this->right_delimiter . ' AS ' . $this->left_delimiter . $v ['AS'] . $this->right_delimiter;
					} else {
						$table_list .= $this->left_delimiter . $v ['table'] . $this->right_delimiter;
					}
				}
				foreach ( $v ['field'] as $k1 => $v1 ) {
					if (isset ( $v1 ['clac'] ) && $v1 ['clac']) {
						if (isset ( $v1 ['AS'] ) && is_string ( $v1 ['AS'] )) {
							$field_list .= strtr ( $k1, array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) ) . ' AS ' . $this->left_delimiter . $v1 ['AS'] . $this->right_delimiter . ',';
						} else {
							$field_list .= strtr ( $k1, array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) ) . ',';
						}
					} else {
						if (is_string ( $v1 ['AS'] )) {
							$field_list .= $this->left_delimiter . $v ['AS'] . $this->right_delimiter . '.' . $this->left_delimiter . $k1 . $this->right_delimiter . ' AS ' . $this->left_delimiter . $v1 ['AS'] . $this->right_delimiter . ',';
						} else {
							$field_list .= $this->left_delimiter . $v ['AS'] . $this->right_delimiter . '.' . $this->left_delimiter . $k1 . $this->right_delimiter . ',';
						}
					}
				}
			}
			$field_list = substr ( $field_list, 0, - 1 );
			$sql .= 'SELECT ' . $field_list . ' FROM ' . $table_list . ' ' . $where . $offsetStr . ';';
		}
		$res = $this->execSql ( $sql );
		if ($onlynum && $res) {
			$this->num_rows = pg_num_rows ( $res );
			pg_free_result ( $res );
			return intval ( $this->num_rows );
		} elseif ($res) {
			$result = array ();
			$t = null;
			for($t = $this->fetch ( $res ); $t;) {
				array_push ( $result, $t );
				$t = $this->fetch ( $res );
			}
			pg_free_result ( $res );
			return $result;
		} else {
			return null;
		}
	}
	/**
	 * 插入一条数据
	 *
	 * 如果有自增字段返回自增ID，否则返回插入结果
	 * 
	 * @param array $matrix 要插入数据的表和数据 
	 * @return int 插入数据的结果
	 */
	public function Insert($matrix) {
		if ($this->halt) {
			$this->putlog ( 'Driver has been halt,please check you config file. ', __FILE__, __LINE__ );
			return null;
		}
		if (! (is_array ( $matrix ['data'] ) && $matrix ['table']) and $this->log) {
			$this->putlog ( 'Adapter data error!.', __FILE__, __LINE__ );
			return null;
		}
		$field_list = "";
		$value_list = "";
		foreach ( $matrix ['data'] as $k => $v ) {
			if (is_string ( $v ['type'] ) && isset ( $v ['data'] )) {
				$field_list .= $this->left_delimiter . $k . $this->right_delimiter . ',';
				if (isset ( $v ['clac'] ) && $v ['clac']) {
					$value_list .= strtr ( $v ['data'], array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) ) . ',';
				} elseif ($v ['type'] === "C") {
					$value_list .= "'{$v['data']}',";
				} elseif ($v ['type'] === 'I') {
					$value_list .= $v ['data'] . ',';
				} else {
					$value_list .= "'" . htmlspecialchars ( $v ['data'] ) . "',";
				}
			}
			continue;
		}
		$field_list = substr ( $field_list, 0, - 1 );
		$value_list = substr ( $value_list, 0, - 1 );
		$sql = 'INSERT INTO ' . $this->left_delimiter . $matrix ['table'] . $this->right_delimiter . ' (' . $field_list . ') VALUES (' . $value_list . ');';
		$res = $this->execSql ( $sql );
		$insert_id = @mysql_insert_id ( $this->link );
		return $insert_id ? $insert_id : $res;
	}
	/**
	 * 更新数据库记录
	 *
	 * @param array $matrix 更新数据库条件
	 * @return MYSQL_RES 更新执行结果
	 */
	public function Update($matrix) {
		if ($this->halt) {
			$this->putlog ( 'Driver has been halt,please check you config file. ', __FILE__, __LINE__ );
			return null;
		}
		if (! (is_array ( $matrix ['data'] ) && $matrix ['table']) and $this->log) {
			$this->putlog ( 'Adapter data error!.', __FILE__, __LINE__ );
			return null;
		}
		$field_set = "";
		foreach ( $matrix ['data'] as $k => $v ) {
			if (is_string ( $v ['type'] ) && isset ( $v ['data'] )) {
				$field_set .= $this->left_delimiter . $k . $this->right_delimiter;
				if ($v ['type'] === "C") {
					$field_set .= "='" . $v ['data'] . "',";
				} elseif ($v ['type'] === 'I') {
					$field_set .= '=' . strtr ( $v ['data'], array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) ) . ',';
				} else {
					$field_set .= "='" . htmlspecialchars ( $v ['data'] ) . "',";
				}
			}
			continue;
		}
		$field_set = substr ( $field_set, 0, - 1 );
		if (is_string ( $matrix ['onwhere'] )) {
			$matrix ['onwhere'] = strtr ( $matrix ['onwhere'], array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) );
			$sql = 'UPDATE ' . $this->left_delimiter . $matrix ['table'] . $this->right_delimiter . ' set ' . $field_set . ' ' . $matrix ['onwhere'] . ';';
		} else {
			$sql = 'UPDATE ' . $this->left_delimiter . $matrix ['table'] . $this->right_delimiter . ' set ' . $field_set . ';';
		}
		$res = $this->execSql ( $sql );
		return $res;
	}
	/**
	 * 删除数据库数据
	 *
	 * @param array $matrix 删除条件和表
	 * @return MYSQL_RES 一般是删除的结果
	 */
	public function Delete($matrix) {
		if ($this->halt) {
			$this->putlog ( 'Driver has been halt,please check you config file. ', __FILE__, __LINE__ );
			return null;
		}
		if (! ((isset ( $matrix ['onwhere'] ) or isset ( $matrix ['truncate'] )) && $matrix ['table']) and $this->log) {
			$this->putlog ( 'Adapter data error!.', __FILE__, __LINE__ );
			return null;
		}
		if (isset ( $matrix ['truncate'] ) && $matrix ['truncate']) {
			$sql = 'TRUNCATE TABLE ' . $this->left_delimiter . $matrix ['table'] . $this->right_delimiter . ';';
		} elseif (is_string ( $matrix ['onwhere'] )) {
			$sql = 'DELETE FROM ' . $this->left_delimiter . $matrix ['table'] . $this->right_delimiter . ' ' . strtr ( $matrix ['onwhere'], array ('[' => $this->left_delimiter, ']' => $this->right_delimiter, '\133' => '[', '\135' => ']' ) ) . ';';
		} else {
			$sql = 'DELETE FROM ' . $this->left_delimiter . $matrix ['table'] . $this->right_delimiter . ' ;';
		}
		$res = $this->execSql ( $sql );
		return $res;
	}
	/**
	 * 返回服务器数据库版本
	 *
	 * 要求有version支持
	 * @return true
	 */
	/**
	 * 数据导入
	 *
	 * @param string $sql SQL语句
	 * @param string $sqlsplit Sql分隔符
	 * @return true
	 */
	public function Import($sql, $sqlsplit = 'SQLSPLIT') {
		if ($this->halt) {
			$this->putlog ( 'Driver has been halt,please check you config file. ', __FILE__, __LINE__ );
			return null;
		}
		$sqlSPLIT = split ( $sqlsplit, strtr ( $sql, array ('[' => $this->left_delimiter, ']' => $this->right_delimiter ) ) );
		foreach ( $sqlSPLIT as $v ) {
			$this->execSql ( $v );
		}
		return true;
	}
	/**
	 * 返回服务器数据库版本
	 *
	 * 要求有version支持
	 * @return true
	 */
	public function getServerVersion() {
		$ver = $this->select ( "Select version() as \"version\";" );
		return $ver [0] ['version'];
	}
	/********************************下面为附加功能********************/
	/**
	 * 记录日志
	 *
	 * @param String $message 日志内容
	 * @param String $file 调用文件名
	 * @param Int $line 调用文件行数
	 * @return bool 是否成功写入日志
	 */
	private function putlog($message, $file, $line) {
		if (! $this->logObj) {
			global $flibcfg;
			$this->logObj = new log ( $flibcfg ['logTimeFormat'], __CLASS__ . '.' . $flibcfg ['logFileName'] );
		}
		return $this->logObj->putlog ( $message, $file, $line );
	}
	/**
	 * 返回本模块信息
	 *
	 * @return string
	 */
	public function printInfo() {
		$moudle = substr ( __CLASS__, 5 );
		$return = '';
		$return .= printRow ( array (array ('class' => 'e', 'rowspan' => '4', 'content' => '<center>' . $moudle . '</center>' ), array ('class' => 'v', 'content' => 'version:$Revision: 118 $' ) ) );
		$return .= printRow ( array (array ('class' => 'v', 'content' => 'Supporting generate select, insert, update, delete, pagination. Query' ) ) );
		$return .= printRow ( array (array ('class' => 'v', 'content' => 'Select support for direct hand-written SQL' ) ) );
		$return .= printRow ( array (array ('class' => 'v', 'content' => 'Export SQL support' ) ) );
		return $return;
	}
}